﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ZLGAPI;
using static Bootloader.zlgcan.UDS_TP;
using static ZLGAPI.ZUDS;

namespace Bootloader.zlgcan
{
    public class UDS_TP
    {
        // UDS工作模式枚举
        public enum UDS_MODE
        {
            DoCAN = 0,
            DoCANFD = 1,
        }

        public enum REQ_ADDR
        {
            PHY_ADDR = 0,
            FUN_ADDR = 1,
        }

        private UInt32 _zudsHandle = 0;// ZUDS句柄
        private readonly Func<uint, byte[], bool> _sendFrame;// 外部注入CAN发送方法
        private readonly ZUDS.OnUDSTransmitDelegate _zudsTransmitCall;
        private readonly uint _phyAddr;
        private readonly uint _funAddr;
        private readonly uint _rspAddr;
        private readonly UDS_MODE _udsMode = UDS_MODE.DoCAN;
        private Action<uint, byte[], bool> _addCanLogFun = null;

        // 构造函数
        public UDS_TP(
            Func<uint, byte[], bool> sendFrame,
            uint phyAddr,
            uint funAddr,
            uint repAddr,
            UDS_MODE udsMode = UDS_MODE.DoCAN)
        {
            _sendFrame = sendFrame;
            _phyAddr = phyAddr;
            _funAddr = funAddr;
            _rspAddr = repAddr;
            _udsMode = udsMode;

            // 释放ZUDS占用
            this.UDS_TP_DEInit();

            // 初始化ZUDS协议栈
            //_zudsHandle = ZUDS.ZUDS_Init((uint)udsMode);
            _zudsHandle = ZUDS.ZUDS_Init(0);// 这块就得是0，无论CAN/CANFD
            // 这东西初始化成功了也是0
            //if (_zudsHandle == 0)
            //{
            //    Debug.WriteLine("ZUDS初始化失败");
            //}

            // 务必初始化ZUDS参数
            this.SetUdsParameters(_zudsHandle);

            // 将 this 作为 ctx 传入
            IntPtr ctx = GCHandle.ToIntPtr(GCHandle.Alloc(this));

            // 保存ZUDS发送函数的引用，防止回收
            _zudsTransmitCall = OnTransmit;

            // 注册ZUDS回调
            ZUDS.ZUDS_SetTransmitHandler(_zudsHandle, ctx, _zudsTransmitCall);
        }

        public void UDS_TP_DEInit()
        {
            // 释放ZUDS占用
            ZUDS.ZUDS_Stop(_zudsHandle);
            ZUDS.ZUDS_Release(0);
            ZUDS.ZUDS_Release(1);
        }

        // 普通发送方法
        public bool SendFrame(uint id, byte[] data)
        {
            return _sendFrame(id, data);
        }

        // 注入log打印方法
        public void SetUpdateLogCall(Action<uint, byte[], bool> addCanLogFun)
        {
            _addCanLogFun = addCanLogFun;
        }

        // ZUDS数据源函数
        public void OnReceive(uint id, byte[] data)
        {
            // 构造 ZUDS_FRAME
            var frame = new ZUDS_FRAME
            {
                id = id,
                data_len = (byte)data.Length,
                data = new byte[64], // 分配64字节，兼容CANFD
            };

            // 拷贝实际数据到 frame.data，最多64字节
            Array.Copy(data, frame.data, Math.Min(data.Length, 64));

            // 分配非托管内存
            int size = Marshal.SizeOf<ZUDS_FRAME>();
            IntPtr ptr = Marshal.AllocHGlobal(size);

            // 将托管结构体复制到非托管内存
            Marshal.StructureToPtr(frame, ptr, false);

            // 调用ZUDS库接收
            ZUDS.ZUDS_OnReceive(_zudsHandle, ptr);

            // 释放非托管内存
            Marshal.FreeHGlobal(ptr);

            // 添加显示
            _addCanLogFun?.Invoke(id, data, false);
        }

        // 请求uds诊断方法
        public async Task<(bool IsPositive, byte[] ResponseData)> UdsRequestAsync(
            REQ_ADDR reqAddr,
            bool isSuppressPosRsp,
            byte serviceId,
            byte[] subFunction = null,
            byte[] parameter = null)
        {
            byte suppressPosRsp = isSuppressPosRsp ? (byte)1 : (byte)0;// 是否抑制肯定响应标志位，0 不抑制

            // 若有，将子服务加入列表
            var paramList = new List<byte>();
            if (subFunction != null && subFunction.Length > 0)
            {
                paramList.AddRange(subFunction);
            }
            // 若有，将服务参数加入列表
            if (parameter != null && parameter.Length > 0)
                paramList.AddRange(parameter);
            byte[] paramData = paramList.ToArray();// 创建Byte数组，将列表转换为数组

            // 构造 ZUDS_REQUEST 结构体
            var zudsReq = new ZUDS.ZUDS_REQUEST
            {
                src_addr = (reqAddr == REQ_ADDR.PHY_ADDR) ? _phyAddr : _funAddr,// 请求地址
                dst_addr = _rspAddr,// 响应地址
                sid = serviceId,// 服务ID
                suppress_response = suppressPosRsp,// 是否抑制响应
                param_len = (uint)paramData.Length,// 参数长度
                param = IntPtr.Zero // 参数指针，非托管内存不能直接赋值，后续再赋值
            };

            // 分配非托管内存
            IntPtr pReq = IntPtr.Zero;
            IntPtr pParam = IntPtr.Zero;
            IntPtr pResp = Marshal.AllocHGlobal(Marshal.SizeOf<ZUDS.ZUDS_RESPONSE>());

            try
            {
                // 将子服务和参数，非托管内存赋值给param
                if (paramData.Length > 0)
                {
                    pParam = Marshal.AllocHGlobal(paramData.Length);
                    Marshal.Copy(paramData, 0, pParam, paramData.Length);
                    zudsReq.param = pParam;
                }

                // 将C#中构建的uds请求结构体，拷贝至非托管内存结构体
                pReq = Marshal.AllocHGlobal(Marshal.SizeOf<ZUDS.ZUDS_REQUEST>());
                Marshal.StructureToPtr(zudsReq, pReq, false);

                // 创建请求响应结构体
                Marshal.WriteInt32(pResp, 0); // 至少清 status 字段

                // 挂起调用防止阻塞ui
                await Task.Run(() =>
                {
                    ZUDS.ZUDS_Request(_zudsHandle, pReq, pResp);
                });

                // 读取响应
                var resp = Marshal.PtrToStructure<ZUDS.ZUDS_RESPONSE>(pResp);

                // 通信失败、超时、总线错误等
                if (resp.status != 0 && isSuppressPosRsp == false)
                {
                    return (IsPositive: false, ResponseData: Array.Empty<byte>());
                }

                // 肯定响应
                if (resp.type == 1)
                {
                    // 肯定响应
                    var pos = resp.union.zudsPositive;
                    if (pos.param_len > 0 && pos.param != IntPtr.Zero)
                    {
                        byte[] data = new byte[pos.param_len + 1];
                        data[0] = (byte)(serviceId + 0x40);
                        Marshal.Copy(pos.param, data, 1, (int)pos.param_len);
                        return (IsPositive: true, ResponseData: data);
                    }
                    return (IsPositive: true, ResponseData: Array.Empty<byte>());// 肯定响应但是没有参数返回
                }
                else
                {
                    // 否定响应
                    byte nrc = resp.union.zudsNegative.error_code;
                    return (IsPositive: false, ResponseData: new byte[] { 0x7F, serviceId, nrc });
                }
            }
            finally
            {
                // === 7. 安全释放所有非托管内存 ===
                if (pParam != IntPtr.Zero)
                    Marshal.FreeHGlobal(pParam);
                if (pReq != IntPtr.Zero)
                    Marshal.FreeHGlobal(pReq);
                if (pResp != IntPtr.Zero)
                    Marshal.FreeHGlobal(pResp);
            }
        }

        // 设置会话维持
        public void SetTesterPresent(bool enable, UInt16 cycle = 2000)
        {
            var param = new ZUDS_TESTER_PRESENT_PARAM
            {
                addr = _funAddr,
                cycle = cycle,
                suppress_response = 1,
                reserved = 0
            };

            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<ZUDS_TESTER_PRESENT_PARAM>());
            try
            {
                Marshal.StructureToPtr(param, ptr, false);
                ZUDS_SetTesterPresent(_zudsHandle, enable ? (byte)1 : (byte)0, ptr);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
        }

        // 初始化ZUDS参数，不初始化有概率会崩溃
        private void SetUdsParameters(uint zudsHandle)
        {
            // 会话层参数
            var sessionParam = new ZUDS_SESSION_PARAM
            {
                timeout = 500,         // 请求超时
                enhanced_timeout = 10000, // 78等待超时
            };

            IntPtr sessionPtr = Marshal.AllocHGlobal(Marshal.SizeOf<ZUDS_SESSION_PARAM>());

            Marshal.StructureToPtr(sessionParam, sessionPtr, false);
            ZUDS.ZUDS_SetParam(zudsHandle, 0, sessionPtr);
            Marshal.FreeHGlobal(sessionPtr);

            byte maxDataLen = 8;
            if (_udsMode == UDS_MODE.DoCANFD)
            {
                maxDataLen = 64;
            }
            // ISO15765 网络层参数
            var isoParam = new ZUDS_ISO15765_PARAM
            {
                version = 1,                    // 使用 2016 版本
                max_data_len = maxDataLen,      // 标准 CAN 8，CANFD 64）
                local_st_min = 0,               // STMin设置为0
                block_size = 0,                 // 不限制块数
                fill_byte = 0x00,               // 未使用位的填充值
                frame_type = 0,                 // 标准帧
                is_modify_ecu_st_min = 0,       // 使用ECU返回的STmin
                remote_st_min = 0,              // 发送多帧时用
                fc_timeout = 100,               // 接收流控超时时间(ms)
                fill_mode = 1,                  // 数据不够填充至该帧DLC长度

                // 必须初始化保留字段！
                reserved = new byte[5] { 0, 0, 0, 0, 0 }
            };

            IntPtr isoPtr = Marshal.AllocHGlobal(Marshal.SizeOf<ZUDS_ISO15765_PARAM>());
            Marshal.StructureToPtr(isoParam, isoPtr, false);
            ZUDS.ZUDS_SetParam(zudsHandle, 1, isoPtr);
            Marshal.FreeHGlobal(isoPtr);

        }

        // 用于ZUDS库中回调的CAN发送方法
        private static uint OnTransmit(IntPtr ctx, IntPtr frame, uint count)
        {
            // 从 ctx 还原 UDS_TP 实例
            GCHandle gch = GCHandle.FromIntPtr(ctx);
            var self = (UDS_TP)gch.Target;

            // 遍历所有待发送的帧
            int frameSize = Marshal.SizeOf<ZUDS_FRAME>();
            for (int i = 0; i < count; i++)
            {
                // frame可能是多帧数据，使用Add方法偏移framePtr指针，等效于C语言的frame[i]
                IntPtr framePtr = IntPtr.Add(frame, i * frameSize);

                // 类型转换
                ZUDS_FRAME frameTx = Marshal.PtrToStructure<ZUDS_FRAME>(framePtr);

                // 提取数据
                byte[] data = new byte[frameTx.data_len];
                Array.Copy(frameTx.data, data, frameTx.data_len);

                // 调用外部发送函数
                bool ok = self._sendFrame(frameTx.id, data);
                if (!ok)
                {
                    Debug.WriteLine("ZUDS发送失败");
                    return 1; // 发送失败，返回非0
                }

                // 添加显示
                self._addCanLogFun?.Invoke(frameTx.id, data, true);
            }
            return 0; // 成功
        }
    }
}
